#include <ESP8266WiFi.h>
#include <DHTesp.h>
#include "Adafruit_MQTT.h".
#include "Adafruit_MQTT_Client.h"

//====================================================================================
#if 1   //1：使用串口打印输出   0：禁止串口输出
  #define coutInit(x) Serial.begin(x)
  #define cout Serial.print
  #define coutln Serial.println
#else
  #define coutInit(x) 
  #define cout(x)      
  #define coutln(x)
#endif 
//====================================================================================
#define WLAN_SSID       "求约得密码"
#define WLAN_PASS       "1234abcd!!!"

void WifiConnect()
{
  cout("\nConnecting to ");
  coutln(WLAN_SSID);
  WiFi.begin(WLAN_SSID, WLAN_PASS);
  int wifiCount = 120;  //60s没有连接上就重启
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    cout(".");
    wifiCount --;
    if(wifiCount <= 0)
    {
      cout("\nReset system......");
      system_restart();
    }
  }
  coutln();
  cout("WiFi connected,IP address: ");
  coutln(WiFi.localIP());
}
//====================================================================================
//服务器启动服务命令： mosquitto -c /etc/mosquitto/mosquitto.conf -d
WiFiClient client;
#define AIO_SERVER      "5.252.164.91"
#define AIO_SERVERPORT  1883                   // use 8883 for SSL
#define AIO_USERNAME    "jtMqtt_1"
#define AIO_KEY         "123456"

Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);

void MQTT_connect(int reconnect = 0)     //失联情况下会自动重新连接
{
  int8_t ret;
  if(!reconnect)
  {
    if (mqtt.connected()) 
      return;
  }

  cout("Connecting to MQTT... ");
  int retries = 10;  //自动重连10次，10次后连不上进入死循环，等待看门狗重启
  while ((ret = mqtt.connect()) != 0)   // connect will return 0 for connected
  { 
       coutln(mqtt.connectErrorString(ret));
       coutln("Retrying MQTT connection in 5 seconds...");
       mqtt.disconnect();
       delay(5000);  
       retries--;
       if (retries <= 0) 
       {
         //while (1); 
          //cout("\nReset system......");
          //system_restart();
          WifiConnect();
       }
  }
  coutln("MQTT Connected!");
}

Adafruit_MQTT_Publish updataCount = Adafruit_MQTT_Publish(&mqtt,"esp_updataCount",0);   //用于监测数据更新次数
bool sendUpdataCount()
{
  static unsigned int runCount = 0;
  cout(F("Sending updataCount val: ")); cout(runCount); cout("...");
  bool res = updataCount.publish(runCount++);
  if(res)
    coutln(F("OK!"));
  else 
    coutln(F("Failed"));
  return res;
}
//======================================== 温湿度传感器 ============================================                                        
#define dhtPin 2
DHTesp dht;
Adafruit_MQTT_Publish pub_temp = Adafruit_MQTT_Publish(&mqtt,"esp_temp",0);
Adafruit_MQTT_Publish pub_hum = Adafruit_MQTT_Publish(&mqtt,"esp_hum",0);

void dht11Init()
{
  coutln("DHT11 initialization...");
  dht.setup(dhtPin, DHTesp::DHT11);
}

float dht_temp; //全局温度变量
float dht_hum;  //全局湿度变量
void getDHT11Data()
{
  TempAndHumidity dhtValues = dht.getTempAndHumidity();
  dht_temp = dhtValues.temperature;
  dht_hum = dhtValues.humidity;
}

bool sendDHT()
{
  getDHT11Data();  //获取温湿度
  cout(F("Sending DHT11 - temp:"));
  cout(dht_temp);cout("...");
  bool res1 = pub_temp.publish(dht_temp);
  if(res1)
    coutln(F("OK!"));
  else 
    coutln(F("Failed"));
    
  cout(F("Sending DHT11 - hum:"));
  cout(dht_hum);cout("...");
  bool res2 = pub_hum.publish(dht_hum);
  if(res2)
    coutln(F("OK!"));
  else 
    coutln(F("Failed"));
    
  return res1|res2;
}
//============================超声波传感器===========================================
#define  trigPin  14
#define  echoPin  12
Adafruit_MQTT_Publish pub_distance = Adafruit_MQTT_Publish(&mqtt,"esp_distance",0);

void distanceInit()
{
  coutln("Ultrasonic initialization...");
  digitalWrite(trigPin, LOW);
  delayMicroseconds(1000);
  digitalWrite(trigPin, HIGH);
  pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
  pinMode(echoPin, INPUT);  // Sets the echoPin as an Input
}
float getDistance()
{
  digitalWrite(trigPin, LOW);  // Clears the trigPin
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);  // Sets the trigPin on HIGH state for 10 micro seconds
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  long duration = pulseIn(echoPin, HIGH);  // Reads the echoPin, returns the sound wave travel time in microseconds
  float distance = duration*0.034/2;  // Calculating the distance
  return distance;
}

bool sendDistance()
{
  float distance = getDistance(); //获取距离
  cout(F("Sending distance:"));
  cout(distance);cout("...");
  bool res = pub_distance.publish(distance);
  if(res)
    coutln(F("OK!"));
  else 
    coutln(F("Failed"));
  return res;
  
}

//=============================== led灯控制 ========================================
#define ledPIN   4    //定义LED IO
Adafruit_MQTT_Subscribe sub_led = Adafruit_MQTT_Subscribe(&mqtt,"esp_led",2);

void ledInit(void)
{
  coutln("LED initialization...");
  pinMode(ledPIN, OUTPUT); 
  digitalWrite(ledPIN, HIGH);     
}
void openLed()
{
  digitalWrite(ledPIN, HIGH);
}
void closeLed()
{   
  digitalWrite(ledPIN, LOW);
}
void ledCallback(double led)
{
  cout(F("Get LED: "));
  coutln(led);
  if(led > 0.5)
    openLed();
  else
    closeLed();
}
//============================= 关强传感器 ==========================================
#define lightPIN   A0    //定义光照强度传感器IO
Adafruit_MQTT_Publish pub_light = Adafruit_MQTT_Publish(&mqtt,"esp_light",0);
void initLight()
{
  coutln("Light initialization...");
  pinMode(lightPIN,INPUT);
}
int getLight()    //0-100
{
  int value = analogRead(lightPIN);
  int percentage = map(value, 0, 1024, 0, 100);
  return percentage;
}
bool sendLight()
{
  int light = getLight(); //获取光强
  cout(F("Sending light:")); cout(light);cout("...");
  bool res = pub_light.publish(light);
  if(res)
    coutln(F("OK!"));
  else 
    coutln(F("Failed"));
  return res;
}
//============================== 人体红外传感器 =========================================
#define infraredPIN   13    //定义人体红外传感器IO
Adafruit_MQTT_Publish pub_people = Adafruit_MQTT_Publish(&mqtt,"esp_people",0);
void initInfrared()
{
  coutln("Infrared initialization...");
  pinMode(infraredPIN,INPUT);
}
bool getInfrared()    //0-100
{
  return digitalRead(infraredPIN);
}
bool sendInfrared()
{
  bool people = getInfrared(); //获取人体红外
  cout(F("Sending people:")); cout(people);cout("...");
  bool res = pub_people.publish(people);
  if(res)
    coutln(F("OK!"));
  else 
    coutln(F("Failed"));
  return res;
}
//=============================== 烟雾传感器 ========================================
#define smogPIN   15    //定义烟雾传感器IO
Adafruit_MQTT_Publish pub_smog = Adafruit_MQTT_Publish(&mqtt,"esp_smog",1);   //烟雾消息比较重要，优先级提高
void initSmog()
{
  coutln("Smog initialization...");
  pinMode(smogPIN,INPUT); 
}
bool getSmog()    //0-100
{
  return digitalRead(smogPIN);
}
bool sendSmog()
{
  bool smog = getSmog(); //获取烟雾
  cout(F("Sending smog:")); cout(smog);cout("...");
  bool res = pub_smog.publish(smog);
  if(res)
    coutln(F("OK!"));
  else 
    coutln(F("Failed"));
  return res;
}
//=======================================================================

void stringCallback(char *data, uint16_t len) 
{
  cout("Hey we're in a onoff callback, the button value is: ");
  coutln(data);
}

void setup() 
{
  coutInit(115200);
  delay(20);
  
  //===== 初始化各种传感器 =====
  dht11Init();
  distanceInit();
  ledInit();
  initLight();
  initInfrared();
  initSmog();
  
  //===== 连接wifi =====
  WifiConnect();
  
  //===== 设置subscribe =====
  //mqtt.subscribe(&onoffbutton);
  sub_led.setCallback(ledCallback);
  mqtt.subscribe(&sub_led);
}

void loop() 
{
  delay(100);
  MQTT_connect();
  mqtt.processPackets(3000);

  int succes_count = 0;   //发送成功计数，用于判断连接状态，MQTT_connect函数短时间无法判断。
  //===== 发送计数 =====
  if(sendUpdataCount())
    succes_count ++;
  //===== 发送温度、湿度（DHT11） =====
  if(sendDHT())
    succes_count ++;
  //===== 发送距离 =====
  if(sendDistance())
    succes_count ++;
  //===== 发送光强 =====
  if(sendLight())
    succes_count ++;
  //===== 发送烟雾 =====
  if(sendSmog())
    succes_count ++;
  //===== 发送人体红外 =====
  if(sendInfrared())
    succes_count ++;
  
  coutln(F("==========================="));
  //===== 发送失败处理方式 =====
  static int sendFaildCount = 0;  //连续全部发送失败次数
  if(succes_count == 0) //表示全部发送失败
  {
    sendFaildCount ++;
    if(sendFaildCount >= 2)
    {
      MQTT_connect(1);  //强制重连
      sendFaildCount = 0;
    }
  }
  else
    sendFaildCount = 0;
}
